package thread.sync;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class CriticalSection {

	public static void main(String[] args) {
		// Test the two different approaches:
		final PairManipulator pm1 = new PairManipulator(new PairManager1());
		final PairManipulator pm2 = new PairManipulator(new PairManager2()); /* 还好测试没将其（PairManager2）用在多线程 */
		new Timer(true).schedule(new TimerTask() {
			@Override
			public void run() {
				System.out.println("pm1: " + pm1);
				System.out.println("pm2: " + pm2);
				System.exit(0);
			}
		}, 500); // run() after 500 milliseconds
	}

}

class Pair {
	private int x, y;

	public Pair(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public Pair() {
		this(0, 0);
	}

	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public void incrementX() {
		x++;
	}

	public void incrementY() {
		y++;
	}

	public String toString() {
		return "x: " + x + ", y:" + y;
	}

	// Arbitrary invariant -- both variables must be equal:
	public void checkState() {
		if (x != y)
			throw new PirValuesNotEqualException();
	}

	public class PirValuesNotEqualException extends RuntimeException {
		public PirValuesNotEqualException() {
			super("Pair values not equal:" + Pair.this);
		}
	}
}

// Protect a Pair inside a thread-safe class:
abstract class PairManager {
	protected Pair p = new Pair();

	private List<Pair> storage = new ArrayList<Pair>();

	public synchronized Pair getPair() {
		// Make a copy to keep the original safe:
		// see：effctive java - 保护性拷贝
		return new Pair(p.getX(), p.getY());
	}

	protected void store() { // 多线程运用时也得考虑下同步问题
		storage.add(getPair());
	}

	// A "template method":
	public abstract void doTask();
}

// Synchronize the entire method:
class PairManager1 extends PairManager {
	@Override
	public synchronized void doTask() {
		p.incrementX();
		p.incrementY();
		store();
	}

}

// Use a critical section: ** 其实这个Manager自己在多线程中也是“自身难保”的，由store()方法引起
class PairManager2 extends PairManager {

	@Override
	public void doTask() {
		synchronized (this) {
			p.incrementX();
			p.incrementY();
		}
		store(); // ?? 这里不会出问题？？
	}
}

class PairManipulator extends Thread {
	private PairManager pm;

	private int checkCounter = 0;

	private class PairChecker extends Thread {
		PairChecker() {
			start();
		}

		@Override
		public void run() {
			while (true) {
				checkCounter++;
				pm.getPair().checkState();
			}
		}
	}

	public PairManipulator(PairManager pm) {
		this.pm = pm;
		start();
		new PairChecker();
	}

	public void run() {
		while (true) {
			pm.doTask();
		}
	}

	public String toString() {
		return "Pair(doTask()所在线程循环次数): " + pm.getPair() + " checkCounter(check线程循环次数)：" + checkCounter;
	}
}